package cn.tqfeiyang.smartadmin.commons.response;

import cn.tqfeiyang.smartadmin.commons.Constants;
import cn.tqfeiyang.smartadmin.commons.toolbox.RequestUtils;
import cn.tqfeiyang.smartadmin.commons.toolbox.ServletUtils;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonInclude;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;

import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * REST API 返回结果
 *
 * @author tqfeiyang
 * @since 2022-3-16
 */
@Data
@Accessors(chain = true)
@Builder
@AllArgsConstructor
@Schema(description = "响应结果")
public class ApiResult<T> implements Serializable {

    private static final long serialVersionUID = 7594052194764993562L;

    @Schema(description = "响应编码，成功为2000", example = "2000")
    private int code;

    @Schema(description = "响应结果 true：成功，false：失败")
    private boolean success;

    @Schema(description = "响应消息", example = "执行成功")
    private String message;

    @Schema(description = "响应数据")
    @JsonInclude(JsonInclude.Include.NON_NULL)
    private T data;

    @Schema(description = "响应时间")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private LocalDateTime time;

    @Schema(description = "日志链路ID")
    @JsonIgnore
    private String traceId;

    //----------------------- 成功 ---------------------------
    public static ApiResult success() {
        return result(ResultCode.SUCCESS, null, null);
    }

    public static ApiResult success(String message) {
        return result(ResultCode.SUCCESS, message, null);
    }

    public static ApiResult success(Object data) {
        return result(ResultCode.SUCCESS, null, data);
    }

    public static ApiResult success(String message, Object data) {
        return result(ResultCode.SUCCESS, message, data);
    }

    //----------------------- 失败 ---------------------------

    public static ApiResult failed() {
        return internalFailed(ResultCode.FAILED, null, null);
    }

    public static ApiResult failed(String message) {
        return internalFailed(ResultCode.FAILED, message, null);
    }

    public static ApiResult failed(Object data) {
        return internalFailed(ResultCode.FAILED, null, data);
    }

    public static ApiResult failed(Throwable throwable) {
        return internalFailed(ResultCode.FAILED, null, convertExceptionToData(throwable));
    }

    public static ApiResult failed(String message, Object data) {
        return internalFailed(ResultCode.FAILED, message, data);
    }

    public static ApiResult failed(String message, Throwable throwable) {
        return internalFailed(ResultCode.FAILED, message, convertExceptionToData(throwable));
    }

    public static ApiResult failed(ResultCode resultCode) {
        return internalFailed(resultCode, null, null);
    }

    public static ApiResult failed(ResultCode resultCode, String message) {
        return internalFailed(resultCode, message, null);
    }

    public static ApiResult failed(ResultCode resultCode, Object data) {
        return internalFailed(resultCode, null, data);
    }

    public static ApiResult failed(ResultCode resultCode, Throwable throwable) {
        return internalFailed(resultCode, null, convertExceptionToData(throwable));
    }

    public static ApiResult failed(ResultCode resultCode, String message, Object data) {
        return internalFailed(resultCode, message, data);
    }

    public static ApiResult failed(ResultCode resultCode, String message, Throwable throwable) {
        return internalFailed(resultCode, message, convertExceptionToData(throwable));
    }

    private static ApiResult internalFailed(ResultCode resultCode, String message, Object data) {
        if (ResultCode.SUCCESS == resultCode) {
            throw new RuntimeException("失败结果状态码不能为" + ResultCode.SUCCESS.getCode());
        }
        return result(resultCode, message, data);
    }

    private static Map<String, Object> convertExceptionToData(Throwable exception) {
        if (RequestUtils.isDebugRequest(ServletUtils.getRequest())) {
            Map<String, Object> dataMap = new HashMap<>();
            if (exception != null) {
                //不要输出异常对象，只显示异常简要信息
                Map<String, Object> exceptionMap = new HashMap<>();
                exceptionMap.put("message", exception.getMessage());
                exceptionMap.put("className", exception.getClass().getCanonicalName());
                dataMap.put("exception", exceptionMap);
            }
            return dataMap;
        } else {
            return null;
        }
    }



//    public static ApiResult failed(BusinessException exception) {
//        if (exception.getResultCode() != null) {
//            return failed(exception.getResultCode(), exception.getMessage(), convertExceptionToData(exception));
//        } else {
//            return failed(ResultCode.FAILED, exception.getMessage(), convertExceptionToData(exception));
//        }
//    }

    //-------------------------------------------------------

    public static ApiResult result(boolean flag) {
        if (flag) {
            return success();
        }
        return failed();
    }

    public static ApiResult result(ResultCode resultCode) {
        return result(resultCode, null);
    }

    public static ApiResult result(ResultCode resultCode, String message) {
        return result(resultCode, message, null);
    }

    public static ApiResult result(ResultCode resultCode, Object data) {
        return result(resultCode, null, data);
    }

    public static ApiResult result(ResultCode resultCode, String message, Object data) {
        if (resultCode == null) {
            throw new RuntimeException("结果状态码不能为空");
        }
        boolean success = false;
        int code = resultCode.getCode();
        if (ResultCode.SUCCESS.getCode() == code) {
            success = true;
        }
        String outMessage;
        if (StringUtils.isBlank(message)) {
            outMessage = resultCode.getMessage();
        } else {
            outMessage = message;
        }
        String traceId = MDC.get(Constants.TRACE_ID);
        return ApiResult.builder()
                .code(code)
                .message(outMessage)
                .data(data)
                .success(success)
                .time(LocalDateTime.now())
                .traceId(traceId)
                .build();
    }

}
